目前在 Next App 中點擊登出後,在嘗試重新登入的時候會發現不會再次跳出先前的 Keycloak 登入畫面,這是因為當 Next 這邊登出時只是清理掉 Next Auth 自己的 Session 紀錄,並沒有將 Keycloak 的 Session 登出,而當重新再登入時因為 Keycloak 還保持著登入狀態所以就跳過輸入視窗直接登入了。
這種作法是刻意的,就像在用了 Facebook 登入某些應用後,你不會想要再登出那隻應用時把 Facebook 也一併登出了。
但因為這次預計是以 Keycloak 中控每個跟主應用銜接的服務的登入狀態,會希望在主應用登出時可以把所有服務都一併登出,所以需要做成同步登出的功能。
這邊需要另外呼叫 Keycloak 的登出端口來實現同步登出,首先做一個處理這件事的 api。
// src/server/api/routers/auth.ts
import { env } from "~/env";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
export const authRouter = createTRPCRouter({
federatedLogout: protectedProcedure.query(async ({ ctx }) => {
const userId = ctx.session.user.id;
if (!userId) {
throw new Error("No user id found");
}
const account = await ctx.db.account.findFirst({
where: {
userId,
provider: "keycloak",
},
});
if (!account) {
throw new Error("No account found");
}
if (!account.id_token) {
throw new Error("No id token found");
}
const logOutUrl = new URL(
`${process.env.KEYCLOAK_ISSUER}/protocol/openid-connect/logout`,
);
logOutUrl.searchParams.set("id_token_hint", account.id_token);
logOutUrl.searchParams.set("post_logout_redirect_uri", env.CLIENT_BASE_URL);
return logOutUrl.toString();
}),
});
在 trpc 中建一個 auth.federatedLogout 的 api ,主要功能:
id_token
跟 keycloak 的站點位置,這些可以從 account 中取得。接著前端這邊新建一個負責登出的按鈕元件。
"use client";
import { signOut } from "next-auth/react";
import { api } from "~/trpc/react";
const ActionSignOut = () => {
const { data } = api.auth.federatedLogout.useQuery();
const onClick = async () => {
if (data) {
await signOut({ redirect: false });
window.location.href = data;
}
};
return (
<>
<button
className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20"
onClick={onClick}
>
{"Sign Out"}
</button>
</>
);
};
export default ActionSignOut;
首先從上面的 api 中取得登出 keycloak 的 url,確認成功後再登出 NextAuth 清空這一端的 session ,再來導向登出 keycloak 的 url ,實現雙邊同步登出。
不過到這邊發現有個問題,在登出又重新登入後,account 中的 id_token 貌似沒更新,導致在嘗試第二次登出時會出現一個確認登出的畫面,因為 id_token 對不上。
看來還需要另外想辦法處理這個 token rotation 的問題,查資料的時候好像也有人說到 refresh token 的處理會很麻煩,得再研究研究。